home *** CD-ROM | disk | FTP | other *** search
- /* shrink.c - minimize 3dv file size */
-
- /* Oscar Garcia <garciao@mof.govt.nz>, May 1992 */
-
- #define NDEBUG
-
- /* uses getopt */
- int getopt(int argc, char *argv[], char *options);
- extern int optind;
- extern char *optarg;
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <math.h>
- #include <assert.h>
- #include <mem.h>
- #include <alloc.h>
-
- #define USAGE "usage: shrink [/on] [/tx] [/q] [input_file [output_file]]\n\
- \tIf file names are omitted, the standard i/o streams are used.\n\
- \tOptions (with defaults):\n\
- \t\t/o3 - Optimization level (1, 2, or 3)\n\
- \t\t/t1e6 - Points closer than (range / xx) considered equal\n\
- \t\t/q - Quiet, no progress info displayed\n"
-
- #define ERROR(msg) {fputs(msg,stderr),exit(1);}
- #define PERROR(msg) {perror(msg),exit(1);}
-
- #define BIG 3e33
- #define ODEFAULT 3
- #define TDEFAULT 1e6
-
- typedef float POINT[3];
- typedef struct
- { int from, to, colour;
- }LINESTR;
-
-
- /* test if two points are close enough */
- int samepoint(POINT p, POINT q, double tol)
- { int i = 3;
- while (i-- > 0)
- if (fabs(*p++ - *q++) > tol)
- return 0;
- return 1;
- }
-
-
- void main(int argc, char* argv[])
- {
- int i, j, k, n, m, p, col, first, last, *newi,
- oplevel = ODEFAULT, quiet = 0;
- double u, tolerance = TDEFAULT;
- unsigned long memory;
- char options[] = "o:t:q";
- FILE *infile = stdin, *outfile = stdout;
- POINT min, max, *x, *this, *seen;
- LINESTR *line;
-
- /* get options */
- while ((n = getopt(argc, argv, options)) != EOF)
- if (n == '?')
- ERROR(USAGE)
- else
- switch (n)
- { case 'o': oplevel = atoi(optarg); break;
- case 't': tolerance = atof(optarg); break;
- case 'q': quiet = 1; break;
- }
- if (oplevel < 1 || oplevel > 3 || tolerance <= 0)
- ERROR(USAGE);
-
- if (!quiet)
- fprintf(stderr, "SHRINK: 3dv file optimizer starting...\n");
-
- /* open files */
- if (optind < argc - 2)
- ERROR(USAGE); /* too many */
- if (optind < argc)
- { infile = fopen(argv[optind], "rt");
- if (infile == NULL)
- ERROR("Can't open input file");
- if (++optind < argc)
- { outfile = fopen(argv[optind], "wt");
- if (outfile == NULL)
- ERROR("Can't open output file");
- }
- }
-
- /* read points */
- i = fscanf(infile, "%d", &n);
- if (i != 1 || ferror(infile))
- PERROR("Bad input file");
- newi = calloc(n + 1, sizeof(int)); /* space for new indices */
- #ifdef __MSDOS__
- if ((unsigned)n * sizeof(POINT) > 0xfff0U)
- ERROR("64k memory block limit exceeded");
- #endif
- x = malloc((unsigned)n * sizeof(POINT));
- if (x == NULL)
- ERROR("Out of memory");
- for (j = 0; j < 3; j++)
- { min[j] = BIG;
- max[j] = -BIG;
- }
- if (!quiet)
- fprintf(stderr, "SHRINK: Reading %d points...\n", n);
- for (i = 0; i < n; i++)
- { fscanf(infile, "%f %f %f", &x[i][0], &x[i][1], &x[i][2]);
- for (j = 0; j < 3; j++)
- { if (x[i][j] < min[j])
- min[j] = x[i][j];
- else if (x[i][j] > max[j])
- max[j] = x[i][j];
- }
- }
- if (ferror(infile))
- PERROR("Error reading input");
-
- /* check for duplicated points */
- if (!quiet)
- fprintf(stderr, "SHRINK: OK, checking for duplicates...\n");
- for (j = 0, u = -BIG; j < 3; j++)
- if (u < max[j] - min[j])
- u = max[j] - min[j];
- tolerance = u / tolerance; /* comparison tolerance */
- for (this = x, i = j = 0; i++ < n; this++)
- { for (seen = this; seen-- > x; )
- if (samepoint(*this, *seen, tolerance))
- { newi[i] = -abs(newi[(unsigned)(seen-x)+1]); /* flag to del.*/
- break;
- }
- if (newi[i] == 0)
- newi[i] = ++j;
- }
-
- /* output points, without duplicates */
- if (!quiet)
- fprintf(stderr, "SHRINK: Writing coordinates for %d points...\n", j);
- fprintf(outfile, "%d\n", j); /* number of points */
- for (i = 0; i < n; i++)
- { if (newi[i + 1] < 0)
- newi[i + 1] = - newi[i + 1];
- else
- fprintf(outfile, "%g %g %g\n", x[i][0], x[i][1], x[i][2]);
- }
- n = j; /* new number of points */
- free(x);
-
- /* read lines, adjusting indices */
- fscanf(infile, "%d", &m); /* number of lines */
- if (!quiet)
- fprintf(stderr, "SHRINK: Reading %d line move/draw commands...\n", m);
- memory = coreleft();
- #ifdef __MSDOS__
- if (memory > 0xfff0U)
- memory = 0xfff0U; /* avoid segment wraparound */
- #endif
- memory = memory / sizeof(LINESTR);
- if (memory > m + 2)
- memory = m + 2; /* upper bound for number of segments */
- line = malloc(memory * sizeof(LINESTR));
- if (line == NULL)
- ERROR("Memory allocation failure"); /* should not happen */
- for (i = 1; m > 0 && i < memory; m--)
- { fscanf(infile, "%d %d", &j, &col);
- j = newi[j];
- if (col == 0)
- line[i].from = j;
- else
- { line[i].to = j;
- line[i].colour = col;
- line[++i].from = j;
- }
- }
- if (i >= memory)
- ERROR("Out of memory");
- line[i].from = -1; /* sentinel */
- m = i - 1; /* number of line segments */
- if (ferror(infile))
- PERROR("Error reading input");
- if (feof(infile))
- ERROR("Unexpected end of file on input");
- fclose(infile);
-
- if (oplevel >1)
- { /* check for duplicated lines */
- if (!quiet)
- fprintf(stderr,
- "SHRINK: Checking %d line segments for duplicates...\n", m);
- k = m; /* count */
- for (i = 1; i <= m; i++)
- for (j = 1; j < i; j++)
- if ((line[i].from == line[j].from && line[i].to == line[j].to)
- || (line[i].from == line[j].to && line[i].to == line[j].from))
- { line[j].colour |= line[i].colour;
- line[i].from = -1; /* flag for deletion */
- k--; /* count */
- break;
- }
- if (!quiet)
- fprintf(stderr, "SHRINK: %d segments left.\n", k);
- }
-
- if (oplevel > 2)
- {
- /* minimize number of moves */
- if (!quiet)
- fprintf(stderr, "SHRINK: Minimizing moves...\n");
- /* Chains of line segments built as linked lists, using line[].to as
- pointers to next segment. Last line[].to in chain in the last point
- number, flagged with a minus sign. Re-use newi[p] to point to chain
- starting (positive) or ending (negative) with point p. Actually,
- use array indices instead of real pointers.
- */
- memset(newi, 0, (n+1) * sizeof(int)); /* zero chain pointers array */
- for (i = 1; i <= m; i++)
- if ((first = line[i].from) > 0) /* valid segment */
- { if ((j = newi[first]) == 0) /* other chains start/end at first? */
- newi[first] = i; /* no, flag this one */
- else if (j < 0)
- { /* chain at -j ends with first, link it to here */
- newi[first] = 0; /* not end of chain anymore */
- first = line[-j].from; /* new start of chain */
- for (j = -j; line[j].to > 0; j = line[j].to)
- ; /* follow chain, last point flagged with minus */
- assert(-line[j].to == line[i].from); /* debugging check */
- line[j].to = i; /* point to current segment */
- }
- else
- { /* chain at j starts with first, reverse it and link */
- assert(line[i].from == line[j].from);
- newi[first] = 0; /* not start of chain anymore */
- p = i; /* point to here */
- while ((first = -line[j].to) < 0) /* reverse */
- { line[j].to = p; /* point to previous segment */
- p = j; /* next one should point to here */
- j = -first; /* next segment index */
- line[p].from = line[j].from;
- }
- line[j].from = first; /* new first segment */
- line[j].to = p;
- newi[first] = j; /* point to it */
- }
-
- /* now link consecutive segments */
- while (line[i+1].from == line[i].to)
- { line[i].to = i + 1;
- i++;
- }
- last = line[i].to; /* last point */
- line[i].to = -last; /* flag end of chain */
-
- /* see if other chains start/end at last */
- if (last == first)
- ; /* but avoid chasing your tail! */
- else if ((j = newi[last]) == 0)
- newi[last] = -newi[first]; /* flag this end */
- else if (j > 0)
- { /* chain at j starts with last, link to it */
- newi[last] = 0; /* not start of chain anymore */
- assert(-line[i].to == line[j].from); /* debugging check */
- line[i].to = j; /* point to it */
- while (line[j].to > 0)
- j = line[j].to; /* follow chain to last point */
- newi[-line[j].to] = -newi[first]; /* end pointer */
- }
- else
- { /* chain at -j ends with last, reverse and link to it */
- newi[last] = 0; /* not end of chain anymore */
- p = -line[j = -j].from; /* new end of chain */
- newi[-p] = - newi[first]; /* end pointer */
- while ((last = -line[j].to) < 0) /* reverse */
- { line[j].to = p; /* point to previous segment */
- p = j; /* next one should point to here */
- j = -last; /* next segment index */
- line[p].from = line[j].from;
- }
- assert(-line[i].to == last); /* check */
- line[j].from = last; /* new first segment */
- line[j].to = p;
- line[i].to = j; /* point to it */
- }
- }
-
- /* output lines */
- /* first count the moves/draws */
- for (m = 0, i = 1; i <= n; i++)
- if (newi[i] > 0)
- { m++; /* move */
- for (j = newi[i]; j > 0; j = line[j].to)
- m++; /* draw */
- }
- fprintf(outfile, "%d\n", m); /* output the count */
- /* now output the moves/draws */
- if (!quiet)
- fprintf(stderr,
- "SHRINK: OK, Writing %d line move/draw commands...\n", m);
- for (i = 1; i <= n; i++)
- if (newi[i] > 0)
- { for (j = newi[i], col = 0; j > 0; j = line[j].to)
- { fprintf(outfile, "%d %d\n", line[j].from, col);
- col = line[j].colour;
- }
- fprintf(outfile, "%d %d\n", -j, col);
- }
- }
-
- else /* optlevel < 3, no lines optimization */
- {
- /* just output the lines */
- for (i = 1, j = -1, n = 0; i <= m; i++) /* count moves/draws */
- if (line[i].from > 0)
- { if (line[i].from != j)
- n++; /* move */
- j = line[i].to;
- n++; /* draw */
- }
- fprintf(outfile, "%d\n", n);
- if (!quiet)
- fprintf(stderr,
- "SHRINK: Writing %d line move/draw commands...\n", n);
- for (i = 1, j = -1; i <= m; i++) /* output moves/draws */
- if (line[i].from > 0)
- { if (line[i].from != j)
- fprintf(outfile, "%d 0\n", line[i].from); /* move */
- j = line[i].to;
- fprintf(outfile, "%d %d\n", j, line[i].colour); /* draw */
- }
- }
-
- if (ferror(outfile))
- PERROR("Error writing output");
- fclose(outfile);
- if (!quiet)
- fprintf(stderr, "SHRINK: Done.\n");
-
- }